﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;

namespace consoleTesting
{
    public class DayTestUpDown
    {
        List<Result> notClosed = new List<Result>();
        List<Result> all = new List<Result>();
        List<Result> closedToClear = new List<Result>();
        int point = 10000;
        decimal oneWeek = 6;
        decimal _aim = 12;
        TimeSpan _openTimeStart = new TimeSpan(10, 0, 0);
        TimeSpan _openTimeEnd = new TimeSpan(10, 3, 0);

        bool customTest = true;
        bool breakTest = true;

        TimeSpan _jpOpen = new TimeSpan(3, 0, 0);
        TimeSpan _jpClose = new TimeSpan(10, 0, 0);

        decimal Aim 
        {
            get 
            {
                return Math.Round(_aim / point, 4);
            }
        }

        public DayTestUpDown(List<Candle> candles)
        {
            Result currentUp = new Result();
            Result currentDown = new Result();
            Candle c = new Candle();
            bool flag = false;
            decimal pullback = 0;
            decimal jpOpenSessionPrice = 0;
            decimal jpCloseSessionPrice = 0;

            for (int i = 0; i < candles.Count; i++)
            {
                if (i == 0)
                    continue;

                c = candles[i];

                if ( !flag && c.Time.TimeOfDay >= _openTimeStart && c.Time.TimeOfDay <=_openTimeEnd)
                {
                    if (c.Time.Date == new DateTime(2011, 10, 7))
                    { 
                        
                    }

                    flag = true;

                    currentUp = new Result();
                    currentDown = new Result();

                    currentUp.OpenDate = currentDown.OpenDate = c.Time;
                    currentUp.CandleNumber = currentDown.CandleNumber = i;
                    currentUp.OpenPrice = currentDown.OpenPrice = candles[i - 1].Close;
                    currentUp.DurationBeforeCloseInDays = currentDown.DurationBeforeCloseInDays = 99999999999999;//to protect against not closed gaps

                    currentUp.Direction = Direction.UP;
                    currentDown.Direction = Direction.DOWN;

                    currentUp.PullbackPoints = currentDown.PullbackPoints = 0;

                    if (customTest)
                    {
                        jpOpenSessionPrice = candles.First(x => x.Time.Date == c.Time.Date && x.Time.TimeOfDay >= _jpOpen).Open;
                        jpCloseSessionPrice = candles[i - 1].Close;

                        if ((jpOpenSessionPrice - jpCloseSessionPrice) < 0)//jp Up
                        {
                            if (breakTest)
                            {
                                notClosed.Add(currentUp);
                                all.Add(currentUp);
                            }
                            else
                            {
                                notClosed.Add(currentDown);
                                all.Add(currentDown);
                            }
                        }

                        if ((jpOpenSessionPrice - jpCloseSessionPrice) > 0)//jp Down
                        {
                            if (breakTest)
                            {
                                notClosed.Add(currentDown);
                                all.Add(currentDown);
                            }
                            else
                            {
                                notClosed.Add(currentUp);
                                all.Add(currentUp);
                            }
                        }

                        if ((jpOpenSessionPrice - jpCloseSessionPrice) == 0)//jp Dodge
                        {
                            notClosed.Add(currentUp);
                            notClosed.Add(currentDown);

                            all.Add(currentUp);
                            all.Add(currentDown);
                        }
                    }
                    else
                    {
                        notClosed.Add(currentUp);
                        notClosed.Add(currentDown);

                        all.Add(currentUp);
                        all.Add(currentDown);
                    }
                }

                if (c.Time.DayOfWeek != candles[i-1].Time.DayOfWeek)
                    flag = false;

                for (int j = 0; j < notClosed.Count; j++)
                {
                    if (notClosed[j].OpenDate.Date == new DateTime(2011, 10, 7))
                    {

                    }

                    if (notClosed[j].Direction == Direction.DOWN)
                    {

                        pullback = Math.Abs(c.High - notClosed[j].OpenPrice) * point;

                        if (pullback > notClosed[j].PullbackPoints)
                            notClosed[j].PullbackPoints = pullback;


                        if (c.Low + Aim <= notClosed[j].OpenPrice)
                            AddGapAsClosed(c, notClosed[j], i);
                    }

                    if (notClosed[j].Direction == Direction.UP)
                    {
                        pullback = Math.Abs(notClosed[j].OpenPrice - c.Low ) * point;

                        if (pullback > notClosed[j].PullbackPoints)
                            notClosed[j].PullbackPoints = pullback;


                        if (c.High - Aim >= notClosed[j].OpenPrice)
                            AddGapAsClosed(c, notClosed[j], i);
                    }
                }

                ClearClosedGaps();
            }

            string fileName = String.Format("DayUpDown/DayUpDown_Fibo_{0}-{1}_Aim_{2}_points_customTest_{3}_breakTest_{4}.xml", candles[0].Time.ToShortDateString().Replace("/", ""), candles[candles.Count - 1].Time.ToShortDateString().Replace("/", ""), _aim.ToString(), customTest, breakTest);
            Stream stream = File.Open(fileName, FileMode.Create);

            System.Xml.Serialization.XmlSerializer xmlSerGaps = new System.Xml.Serialization.XmlSerializer(all.GetType());
            xmlSerGaps.Serialize(stream, all);

            stream.Close();


            List<Result> sortedList = all;
            int allCount = all.Count;
            String fileStats = String.Format("DayUpDown/DayUpDown_Fibo_{0}-{1}_Stats_{2}_customTest_{3}_breakTest_{4}.txt", candles[0].Time.ToShortDateString().Replace("/", ""), candles[candles.Count - 1].Time.ToShortDateString().Replace("/", ""), _aim, customTest, breakTest);
            StreamWriter sw = File.CreateText(fileStats);

            sw.WriteLine("Цель: {0} пунктов", _aim);
            sw.WriteLine("Расширенный тест: {0}, тест на пробой (смотреть если расширенный тест true) - {1}", customTest, breakTest);

            sw.WriteLine(String.Format("Средний откат: {0} пунктов", Math.Round(sortedList.Average(x => x.PullbackPoints), 2).ToString()));
            sw.WriteLine(String.Format("Максимальный откат: {0} пунктов", Math.Round(sortedList.Max(x => x.PullbackPoints), 2).ToString()));
            sw.WriteLine(String.Format("Минимальный откат: {0} пунктов", Math.Round(sortedList.Min(x => x.PullbackPoints), 2).ToString()));
            sw.WriteLine();

            
            sw.WriteLine();
            int less50 = sortedList.FindAll(x => x.PullbackPoints < 50).Count;
            int c50_100 = sortedList.FindAll(x =>  x.PullbackPoints >= 50 && x.PullbackPoints < 100).Count;
            int c100_200 = sortedList.FindAll(x =>  x.PullbackPoints >= 100 && x.PullbackPoints < 200).Count;
            //int c100_125 = sortedList.FindAll(x => x.GapDurationBeforeCloseInDays < oneWeek && x.PullbackPoints >= 100 && x.PullbackPoints < 125).Count;
            //int c125_150 = sortedList.FindAll(x => x.GapDurationBeforeCloseInDays < oneWeek && x.PullbackPoints >= 125 && x.PullbackPoints < 150).Count;
            //int c150_175 = sortedList.FindAll(x => x.GapDurationBeforeCloseInDays < oneWeek && x.PullbackPoints >= 150 && x.PullbackPoints < 175).Count;
            //int c175_200 = sortedList.FindAll(x => x.GapDurationBeforeCloseInDays < oneWeek && x.PullbackPoints >= 175 && x.PullbackPoints < 200).Count;
            int c200_300 = sortedList.FindAll(x =>  x.PullbackPoints >= 200 && x.PullbackPoints < 300).Count;
            int c300_400 = sortedList.FindAll(x =>  x.PullbackPoints >= 300 && x.PullbackPoints < 400).Count;
            int c400_500 = sortedList.FindAll(x =>  x.PullbackPoints >= 400 && x.PullbackPoints < 500).Count;
            int c500_600 = sortedList.FindAll(x =>  x.PullbackPoints >= 500 && x.PullbackPoints < 600).Count;
            int c600_700 = sortedList.FindAll(x =>  x.PullbackPoints >= 600 && x.PullbackPoints < 700).Count;
            int c700_800 = sortedList.FindAll(x =>  x.PullbackPoints >= 700 && x.PullbackPoints < 800).Count;
            int c800_900 = sortedList.FindAll(x =>  x.PullbackPoints >= 800 && x.PullbackPoints < 900).Count;
            int c900_1000 = sortedList.FindAll(x =>  x.PullbackPoints >= 900 && x.PullbackPoints < 1000).Count;
            int cMore1000 = sortedList.FindAll(x =>  x.PullbackPoints >= 1000).Count;

            sw.WriteLine(String.Format("% позиций с откатом до 50 пунктов: {0} %", Math.Round((decimal)less50 / allCount * 100, 2)));
            sw.WriteLine(String.Format("% позиций с откатом до 100 пунктов: {0} %", Math.Round((decimal)c50_100 / allCount * 100, 2)));
            sw.WriteLine(String.Format("% позиций с откатом до 200 пунктов: {0} %", Math.Round((decimal)c100_200 / allCount * 100, 2)));
            //sw.WriteLine(String.Format("% позиций с откатом до 125 пунктов: {0} %", Math.Round((decimal)c100_125 / close0WeekCount * 100, 2)));
            //sw.WriteLine(String.Format("% позиций с откатом до 150 пунктов: {0} %", Math.Round((decimal)c125_150 / close0WeekCount * 100, 2)));
            //sw.WriteLine(String.Format("% позиций с откатом до 175 пунктов: {0} %", Math.Round((decimal)c150_175 / close0WeekCount * 100, 2)));
            //sw.WriteLine(String.Format("% позиций с откатом до 200 пунктов: {0} %", Math.Round((decimal)c175_200 / close0WeekCount * 100, 2)));
            sw.WriteLine(String.Format("% позиций с откатом до 300 пунктов: {0} %", Math.Round((decimal)c200_300 / allCount * 100, 2)));
            sw.WriteLine(String.Format("% позиций с откатом до 400 пунктов: {0} %", Math.Round((decimal)c300_400 / allCount * 100, 2)));
            sw.WriteLine(String.Format("% позиций с откатом до 500 пунктов: {0} %", Math.Round((decimal)c400_500 / allCount * 100, 2)));
            sw.WriteLine(String.Format("% позиций с откатом до 600 пунктов: {0} %", Math.Round((decimal)c500_600 / allCount * 100, 2)));
            sw.WriteLine(String.Format("% позиций с откатом до 700 пунктов: {0} %", Math.Round((decimal)c600_700 / allCount * 100, 2)));
            sw.WriteLine(String.Format("% позиций с откатом до 800 пунктов: {0} %", Math.Round((decimal)c700_800 / allCount * 100, 2)));
            sw.WriteLine(String.Format("% позиций с откатом до 900 пунктов: {0} %", Math.Round((decimal)c800_900 / allCount * 100, 2)));
            sw.WriteLine(String.Format("% позиций с откатом до 1000 пунктов: {0} %", Math.Round((decimal)c900_1000 / allCount * 100, 2)));
            sw.WriteLine(String.Format("% позиций с откатом более 1000 пунктов: {0} %", Math.Round((decimal)cMore1000 / allCount * 100, 2)));
            sw.WriteLine();
            //sw.WriteLine(String.Format("Сумма пунктов позиций с откатом до 100 пунктов: {0} пунктов", Math.Round(sortedList.FindAll(x => x.PullbackPoints < 200 && x.GapDurationBeforeCloseInDays == 0).Sum(y => y.GapWidth), 2)));
            //int maxLossCount = 0;
            //int maxProfitCount = 0;
            //double avgLossCount = 0;
            //double avgProfitCount = 0;
            //ConsecuteviLossAndProfit(sortedList, 200, out maxLossCount, out maxProfitCount, out avgLossCount, out avgProfitCount);
            //sw.WriteLine(String.Format("Фильтр {0} пунктов - максимальная серия убытков: {1}, максимальная серия прибыли: {2}, средняя убыточная серия: {3}, средняя прибыльная серия: {4}", 200, maxLossCount, maxProfitCount, avgLossCount, avgProfitCount));
            //sw.WriteLine();

            foreach (int year in sortedList.Select(x => x.OpenDate.Year).Distinct())
                WritePullacksByYear(sw, sortedList, year);

            sw.WriteLine("Всего позиций найдено: {0}", sortedList.Count);
            sw.WriteLine(String.Format("Лет посчитано: {0}", (candles[candles.Count - 1].Time.Year - candles[0].Time.Year).ToString()));
            sw.Close();

        }

        private void WritePullacksByYear(StreamWriter sw, List<Result> sortedList, int year)
        {
            int thisYearCount = sortedList.FindAll(x => x.OpenDate.Year == year).Count;
            int less50 = sortedList.FindAll(x => x.PullbackPoints < 50 && x.OpenDate.Year == year).Count;
            int c50_100 = sortedList.FindAll(x => x.PullbackPoints >= 50 && x.PullbackPoints < 100 && x.OpenDate.Year == year).Count;
            int c100_200 = sortedList.FindAll(x => x.PullbackPoints >= 100 && x.PullbackPoints < 200 && x.OpenDate.Year == year).Count;
            int c100_125 = sortedList.FindAll(x => x.PullbackPoints >= 100 && x.PullbackPoints < 125 && x.OpenDate.Year == year).Count;
            int c125_150 = sortedList.FindAll(x => x.PullbackPoints >= 125 && x.PullbackPoints < 150 && x.OpenDate.Year == year).Count;
            int c150_175 = sortedList.FindAll(x => x.PullbackPoints >= 150 && x.PullbackPoints < 175 && x.OpenDate.Year == year).Count;
            int c175_200 = sortedList.FindAll(x => x.PullbackPoints >= 175 && x.PullbackPoints < 200 && x.OpenDate.Year == year).Count;
            int c200_300 = sortedList.FindAll(x => x.PullbackPoints >= 200 && x.PullbackPoints < 300 && x.OpenDate.Year == year).Count;
            int c300_400 = sortedList.FindAll(x => x.PullbackPoints >= 300 && x.PullbackPoints < 400 && x.OpenDate.Year == year).Count;
            int c400_500 = sortedList.FindAll(x => x.PullbackPoints >= 400 && x.PullbackPoints < 500 && x.OpenDate.Year == year).Count;
            int c500_600 = sortedList.FindAll(x => x.PullbackPoints >= 500 && x.PullbackPoints < 600 && x.OpenDate.Year == year).Count;
            int c600_700 = sortedList.FindAll(x => x.PullbackPoints >= 600 && x.PullbackPoints < 700 && x.OpenDate.Year == year).Count;
            int c700_800 = sortedList.FindAll(x => x.PullbackPoints >= 700 && x.PullbackPoints < 800 && x.OpenDate.Year == year).Count;
            int c800_900 = sortedList.FindAll(x => x.PullbackPoints >= 800 && x.PullbackPoints < 900 && x.OpenDate.Year == year).Count;
            int c900_1000 = sortedList.FindAll(x => x.PullbackPoints >= 900 && x.PullbackPoints < 1000 && x.OpenDate.Year == year).Count;
            int cMore1000 = sortedList.FindAll(x => x.PullbackPoints >= 1000 && x.OpenDate.Year == year).Count;

            sw.WriteLine();
            //sw.WriteLine("Общее кол-во сортированных позиций в {0} году: {1} шт", year, sortedList.FindAll(x => x.OpenDate.Year == year).Count);
            //sw.WriteLine("Кол-во позиций закрывшихся c откатом до 200 пунктов в {0} году: {1} шт", year, sortedList.FindAll(x => x.GapDate.Year == year && x.PullbackPoints < 200).Count);
            //sw.WriteLine("Сумму позиций с откатом до 200 пунктов в {0} году: {1}", year, Math.Round(sortedList.FindAll(x => x.GapDate.Year == year && x.PullbackPoints < 200).Sum(y=>y.GapWidth),2));

            sw.WriteLine(String.Format("Кол-во позиций с откатом до 50 пунктов в {0} году: {1} %", year, Math.Round((decimal)less50 / thisYearCount * 100, 2)));
            sw.WriteLine(String.Format("Кол-во позиций с откатом до 100 пунктов в {0} году: {1} %", year, Math.Round((decimal)c50_100 / thisYearCount * 100, 2)));
            sw.WriteLine(String.Format("Кол-во позиций с откатом до 200 пунктов в {0} году: {1} %", year, Math.Round((decimal)c100_200 / thisYearCount * 100, 2)));
            //sw.WriteLine(String.Format("Кол-во позиций с откатом до 125 пунктов: {0} %", Math.Round((decimal)c100_125 / close0WeekCount * 100, 2)));
            //sw.WriteLine(String.Format("Кол-во позиций с откатом до 150 пунктов: {0} %", Math.Round((decimal)c125_150 / close0WeekCount * 100, 2)));
            //sw.WriteLine(String.Format("Кол-во позиций с откатом до 175 пунктов: {0} %", Math.Round((decimal)c150_175 / close0WeekCount * 100, 2)));
            //sw.WriteLine(String.Format("Кол-во позиций с откатом до 200 пунктов: {0} %", Math.Round((decimal)c175_200 / close0WeekCount * 100, 2)));
            sw.WriteLine(String.Format("Кол-во позиций с откатом до 300 пунктов в {0} году: {1} %", year, Math.Round((decimal)c200_300 / thisYearCount * 100, 2)));
            sw.WriteLine(String.Format("Кол-во позиций с откатом до 400 пунктов в {0} году: {1} %", year, Math.Round((decimal)c300_400 / thisYearCount * 100, 2)));
            sw.WriteLine(String.Format("Кол-во позиций с откатом до 500 пунктов в {0} году: {1} %", year, Math.Round((decimal)c400_500 / thisYearCount * 100, 2)));
            sw.WriteLine(String.Format("Кол-во позиций с откатом до 600 пунктов в {0} году: {1} %", year, Math.Round((decimal)c500_600 / thisYearCount * 100, 2)));
            sw.WriteLine(String.Format("Кол-во позиций с откатом до 700 пунктов в {0} году: {1} %", year, Math.Round((decimal)c600_700 / thisYearCount * 100, 2)));
            sw.WriteLine(String.Format("Кол-во позиций с откатом до 800 пунктов в {0} году: {1} %", year, Math.Round((decimal)c700_800 / thisYearCount * 100, 2)));
            sw.WriteLine(String.Format("Кол-во позиций с откатом до 900 пунктов в {0} году: {1} %", year, Math.Round((decimal)c800_900 / thisYearCount * 100, 2)));
            sw.WriteLine(String.Format("Кол-во позиций с откатом до 1000 пунктов в {0} году: {1} %", year, Math.Round((decimal)c900_1000 / thisYearCount * 100, 2)));
            sw.WriteLine(String.Format("Кол-во позиций с откатом более 1000 пунктов в {0} году: {1} %", year, Math.Round((decimal)cMore1000 / thisYearCount * 100, 2)));
            sw.WriteLine("***************");
        }

        //private void ConsecuteviLossAndProfit(List<Order> sortedList, int sortPullback, out int maxLossCount, out int maxProfitCount, out double avgLossCount, out double avgProfitCount)
        //{
        //    List<int> losses = new List<int>();
        //    List<int> profits = new List<int>();

        //    int curLossCount = 0;
        //    int curProfitCount = 0;

        //    for (int i = 0; i < sortedList.Count; i++)
        //    {
        //        if (sortedList[i].PullbackPoints > sortPullback || sortedList[i].GapDurationBeforeCloseInDays > oneWeek)
        //        {
        //            profits.Add(curProfitCount);
        //            curProfitCount = 0;
        //            curLossCount++;
        //        }
        //        else
        //        {
        //            losses.Add(curLossCount);
        //            curLossCount = 0;
        //            curProfitCount++;
        //        }

        //    }

        //    maxLossCount = losses.Max();
        //    avgLossCount = losses.Average();
        //    maxProfitCount = profits.Max();
        //    avgProfitCount = profits.Average();
        //}

        

        private void AddGapAsClosed(Candle curCandle, Result current, int curCandleNumber)
        {
            current.DayClosed = curCandle.Time.DayOfWeek;
            current.CloseDate = curCandle.Time;
            current.DurationBeforeCloseInDays = Math.Round((decimal)(curCandleNumber - current.CandleNumber)/720,1);

            int place = all.FindIndex(x => x.CandleNumber == current.CandleNumber && x.Direction == current.Direction);
            all.RemoveAt(place);
            all.Insert(place, current);

            closedToClear.Add(current);
        }

        private void ClearClosedGaps()
        {
            foreach (Result g in closedToClear)
                notClosed.Remove(g);

            closedToClear.Clear();
        }

    }
}
